use crate::action_factory::{ActionTrait, ActionData};
use std::error::Error;
use log::*;
use simple_error::*;
use crate::action_do::{executor_fail, executor_recover};
use curl::easy::{Easy, List};
use std::time::Duration;
use std::sync::{Arc,Mutex};
use isahc::config::{Configurable, SslOption, RedirectPolicy};
use isahc::http::header::USER_AGENT;
use crate::config::ActionConf;

pub struct ActionHttp {
    task:ActionData
}

impl ActionHttp {
    // 这里使用库isahc
    fn executor_isahc(&mut self) {
        let task_conf = &self.task.task_action;
        let req = isahc::HttpClientBuilder::new()
            .timeout(Duration::from_millis(task_conf.timeout as u64))
            .default_header(USER_AGENT.as_str(),
                              "User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) \
                              AppleWebKit/537.36 (KHTML, like Gecko) \
                              Chrome/88.0.4324.150 Safari/537.36 \
                              TickChecker/1.0.0")
            .redirect_policy(RedirectPolicy::Limit(2))
            .auto_referer()
            .ssl_options(if task_conf.skip_tls_check { SslOption::DANGER_ACCEPT_REVOKED_CERTS } else { SslOption::NONE })
            .build().unwrap();

        let r = req.head(task_conf.target.as_str());

        match r {
            Ok(resp) => {
                let status_code = resp.status().as_u16();
                if (status_code >= 200 && status_code <= 220) || (status_code >= 300 && status_code <= 305) {
                    if task_conf.output_result {
                        info!("http status:{}\n\theaders:{:?}",status_code,resp.headers());
                    }

                    self.task.do_recover();
                }else {
                    // 失败
                    warn!("http error status code:{}",status_code);
                    self.task.do_failed();
                }
            },
            Err(e) => {
                // 失败
                warn!("http error:{}",e);
                self.task.do_failed();
            }
        }
    }

    // 这里使用curl来进行读写验证
    fn executor_curl(&mut self) {
        let task_conf = &self.task.task_action;
        let mut easy = Easy::new();
        easy.max_redirections(1);
        easy.follow_location(true);
        if task_conf.skip_tls_check {
            easy.ssl_verify_peer(false);
            easy.ssl_verify_host(false);
        }

        let mut headers = List::new();
        headers.append("User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/88.0.4324.150 Safari/537.36 TickChecker/1.0.0");
        easy.http_headers(headers);

        easy.timeout(Duration::from_millis(task_conf.timeout as u64));
        //easy.custom_request("HEAD");

        let mut header_data = Arc::new(Mutex::new(String::new()));
        let mut hd = header_data.clone();
        easy.header_function(move |header| {
            let mut v = hd.lock().unwrap();
            v.push_str(format!("{}", std::str::from_utf8(header).unwrap()).as_str());
            true
        }).unwrap();
        easy.url(task_conf.target.as_str()).unwrap();

        let r = easy.perform();

        match r {
            Ok(_) => {
                if task_conf.output_result {
                    info!("connect timd:{:?},total time:{:?}",
                          easy.connect_time().unwrap(),
                          easy.total_time().unwrap());

                    info!("write header:{}",header_data.lock().unwrap().as_str());
                }

                self.task.do_recover();
            }
            Err(e) => {
                error!("http error:{}",e);
                self.task.do_failed();
            }
        }
    }
}

impl ActionTrait for ActionHttp {
    fn id(&self) -> i32 {
        self.task.id
    }

    fn name(&self) -> &str {
        self.task.name.as_str()
    }

    fn append_data(&mut self, data: ActionData) {
        self.task = data;
    }

    fn executor(&mut self) {
        // 执行Http操作
        info!("start http action:{} id:{}...",self.task.id,self.task.name);
        self.executor_curl(); // 目前使用curl
       // self.executor_isahc();
    }
}

pub fn new_http(data: ActionData) -> ActionHttp {
    ActionHttp { task:data }
}